#ifdef __QT5__TARGET

#include <QtCore/QStringList>

#else

#include <QStringList>

#endif

#include "korlapadmin_model.h"
#include <QVariant>
korlapAdmin_model::korlapAdmin_model()
{
	this->pm = new PatientManager();
	this->fm = new FilterManager();
	this->vm = new VisitManager();
	this->sm = new ScheduleManager();
	this->weekRange = calculateDaysOfThisWeek();
	_initializeTimeTable();
	isLoaded = false;
}

korlapAdmin_model::~korlapAdmin_model()
{
	try
	{
		if(db->isOpen())
		{
			db->close();
			QSqlDatabase::removeDatabase(db->connectionName());
		}

		if(!db->isOpen())
		{
			delete db;
			db = NULL;
		}

		if(timeTableMap != NULL)
		{
			QMap<QDateTime,QDateTime***>::iterator it = timeTableMap->begin();
			while(it != this->timeTableMap->end())
			{
				if(it.value() != NULL)
				{
					for(int i = 0; i < 64;++i)
					{
						for(int j = 0; j < 6; ++j)
						{
							if(it.value()[i][j] != NULL)
							{
								delete it.value()[i][j];
								it.value()[i][j] = NULL;
							}
						}
						delete[] it.value()[i];
					}
					delete[] it.value();
					it.value() = NULL;
				}
				it++;
			}

			delete timeTableMap;
			timeTableMap = NULL;
		}

		if(weekRange != NULL)
		{
			if(weekRange->first != NULL)
			{
				delete weekRange->first;
			}

			if(weekRange->second != NULL)
			{
				delete weekRange->second;
			}
			
			delete weekRange;
		}
		
		if(pm != NULL)
		{
			delete pm;
			pm = NULL;
		}

		if(fm != NULL)
		{
			delete fm;
			fm = NULL;
		}

		if(vm != NULL)
		{
			delete vm;
			vm = NULL;
		}

		if(sm != NULL)
		{
			delete sm;
			sm = NULL;
		}

		if(locale != NULL)
		{
			delete locale;
			locale = NULL;
		}

	}
	catch(int i)
	{
		Exception e;
		e.setMessage("Fatal error: Memory corruption while trying to delete the model");
		e.setErrorCode(i);
		throw e;
	}
}

QDateTime*** korlapAdmin_model::getCurrentTimeTable()
{
	return this->currentTimeTable;
}

QDateTime*** korlapAdmin_model::getTimeTable(QDateTime *dt)
{
	return this->timeTableMap->value(*dt);
}
QPair<QDateTime*,QDateTime*>* korlapAdmin_model::calculateDaysOfThisWeek()
{
	return sm->getThisWeek();
}

void korlapAdmin_model::loadCurrentLocale()
{
	QSqlQuery query;
	query.prepare("select config_value from config where config_name='locale'");
	if(!query.exec())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not query locale settings:");
		throw e;
	};
	if(!query.next())
	{
		Exception e;
		e.setMessage("No locale settings in the database. Using default locale...");
		throw e;
	}
	bool ok;
	int loc = query.value(0).toInt(&ok);
	if(!ok)
	{
		CouldNotCastException e;
		e.setMessage("Could not cast to int when querying locale");
		throw e;
	}

	
	this->locale = new QLocale((QLocale::Language)31,(QLocale::Country)loc);
}

QLocale* korlapAdmin_model::getCurrentLocale()
{
	return this->locale;
}
VisitManager* korlapAdmin_model::getVisitManager()
{
	return vm;
}

int korlapAdmin_model::init()
{
	return pm->init();
}

QMap<QDateTime*,Schedule*>* korlapAdmin_model::getScheduleMapForThisWeek()
{

	if(sm == NULL)
	{
		NullPointerException e;
		e.setMessage("Fatal error: schedule map is null");
		throw e;
	}
	if(!sm->thisWeekLoaded())
		return this->getScheduleMap(sm->getCurrentWeek());
	return sm->getScheduleMapForThisWeek();

}

QList<Schedule*> korlapAdmin_model::getSchedulesForThisWeek()
{
	if(!sm->thisWeekLoaded())
		return getSchedules(sm->getCurrentWeek());
	return sm->getSchedulesForThisWeek();
}

QMap<QDateTime*,Schedule*>* korlapAdmin_model::getScheduleMap(QPair<QDateTime*,QDateTime*> *dates)
{
		QSqlQuery query;
	bool ok = query.prepare("select r.reserv_id, v.visit_id,r.start_date,r.end_date,r.note, v.file_nr, v.follow_up, v.edit_finished, v.edited from reservation r INNER JOIN visit v ON r.visit_id = v.visit_id where r.start_date >= :start and r.start_date <= :end");
	//bool ok = query.prepare("select visit_id,file_Nr,visit_date from visit where visit_date > TO_DATE('" + from +"','yyyy-mm-dd-hh24-mi-ss') and visit_date < to_date('" + to + "','yyyy-mm-dd-hh24-mi-ss')");
	if(!ok)
	{
		Exception e;
		e.setMessage("Could not prepare query");
		throw e;
	}

	
	query.bindValue(":start",*dates->first);
	query.bindValue(":end",*dates->second);

	if(!query.exec())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not Load Schedules. " + query.executedQuery() + query.lastError().text());
		throw e;
	}

	while(query.next())
	{
		unsigned int reserv_id = query.value(0).toUInt();
		unsigned int visit_id = query.value(1).toUInt();
		QDateTime *start_date = new QDateTime(query.value(2).toDateTime());
		QDateTime* end_date = new QDateTime(query.value(3).toDateTime());
		QString note = query.value(4).toString();
		unsigned int fileno = query.value(5).toUInt();
		bool follow_up = query.value(6).toBool();
		bool edit_finished = query.value(7).toBool();
		bool edited = query.value("edited").toBool();	
		if(!start_date->isValid() || !end_date->isValid())
		{
			CouldNotCastException e;
			e.setMessage("could not cast reservation date");
			throw e;
		}

		Visit* visit = vm->getVisit(visit_id);
		
		if(visit == NULL)
		{
			visit = new Visit(visit_id,fileno,*start_date,follow_up,edit_finished);
			visit->setEdited(edited);
			vm->addVisit(visit);
		}
		
		Schedule* schedule = sm->getSchedule(reserv_id);
		if(schedule == NULL)
		{
			schedule = new Schedule(reserv_id,start_date,end_date,visit,note);
			sm->addSchedule(schedule);
		}		
	}
	return sm->getScheduleMapForThisWeek();
}

QList<Schedule*> korlapAdmin_model::getSchedules(QPair<QDateTime*,QDateTime*>* dates)
{
	if(!isLoaded)
	{
		isLoaded = true;
		return this->getScheduleMap(dates)->values();
	}

	QList<Schedule*> ret;
	QString d1 = dates->first->toString();
	QString d2 = dates->second->toString();
	QMap<QDateTime*,Schedule*>::iterator it = sm->getScheduleMapForThisWeek()->lowerBound(dates->first);
	QMap<QDateTime*,Schedule*>::iterator end = sm->getScheduleMapForThisWeek()->upperBound(dates->second);
	end++;
	for(;it != end; ++it)
	{
		ret.append(it.value());
	}
	
	return ret;
}

int korlapAdmin_model::connectToDB(const QString& hostName,const QString& SID, const QString& user, const QString & password)
{
	db = new QSqlDatabase(QSqlDatabase::addDatabase("QPSQL"));
	db->setHostName(hostName);
	db->setDatabaseName(SID);
	db->setUserName(user);
	db->setPassword(password);
	if(db && !db->isOpen())
	{
		if(!db->open())
		{
			CouldNotConnectToDatabaseException e;
			e.setMessage(db->lastError().text());
			delete db;
			db = NULL;
			throw e;
			return -255;
		}
	}
	return 0;
}

void korlapAdmin_model::loadFilters()
{
	QSqlQuery query;
	query.prepare("select * from filters left outer join patient on filters.patient_id=patient.file_nr");
	if(!query.exec())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not query filters: " + query.lastError().text());
		throw e;
	}

	while(query.next())
	{
		bool ok = false;
		unsigned int id = query.value(0).toUInt(&ok);
		if(!ok)
		{
			CouldNotCastException e;
			e.setMessage("Could not cast int in filters");
			throw e;
		}
		QString filterName = query.value(2).toString();
		bool is_default = query.value(3).toBool();
		QDate from = query.value(4).toDate();
		if(!from.isValid())
		{
			CouldNotCastException e;
			e.setMessage("Could not cast from date in filters");
			throw e;
		}

		QDate to = query.value(5).toDate();
		if(!to.isValid())
		{
			CouldNotCastException e;
			e.setMessage("Could not cast to date in filters");
			throw e;
		}

		bool onlyFirstVisits = query.value(6).toBool();
		bool onlyLastVisits = query.value(7).toBool();
		bool onlyFollowUps = query.value(8).toBool();
		const Patient* p = NULL;
		int patient_id= query.value(1).toInt();
		if(patient_id > 0)
		{
			unsigned int fileno = query.value(9).toUInt();
			QString phone = query.value(10).toString();
			QString patientName = query.value(11).toString();
			QDate dob = query.value(12).toDate();
			p = this->pm->findPatientByFileNumber(fileno);
			//p = new Patient(fileno,patientName,phone,dob);
		}

		Filter* f = new Filter();
		f->setDefault(is_default);
		f->setId(patient_id);
		f->setName(filterName);
		f->setPatient(p);
		f->setFrom(from);
		f->setTo(to);
		f->setFirstVisits(onlyFirstVisits);
		f->setLastVisits(onlyLastVisits);
		f->setOnlyFollowUps(onlyFollowUps);
		this->fm->addFilter(f);
		if(is_default)
			this->fm->setDefaultFilter(filterName);
	}
}

void korlapAdmin_model::_initializeTimeTable()
{
	this->currentTimeTable = new QDateTime**[64];
	for(int i = 0; i < 65; ++i)
	{
		currentTimeTable[i] = new QDateTime*[6];
		for(int j = 0; j < 6; ++ j)
		{
			currentTimeTable[i][j] = new QDateTime(weekRange->first->addDays(j).addSecs(900*i));	
		}
	}
	timeTableMap = new QMap<QDateTime,QDateTime***>();
	timeTableMap->insert(*weekRange->first,currentTimeTable);
}


void korlapAdmin_model::addSchedule(int file_nr,QDateTime* startDate, QDateTime* endDate, const QString& note)
{
	QSqlDatabase::database().transaction();
	QSqlQuery query;
	QString sql = "INSERT INTO VISIT (file_nr,visit_date) values (:file_nr,:visit_date) returning visit_id";
	query.prepare(sql);
	query.bindValue(":file_nr",file_nr);
	query.bindValue(":visit_date",*startDate);
	if(!query.exec())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not insert visit. " + query.lastError().text());
		throw e;
	}

	if(!query.next())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not query the visit_id " + query.lastError().text());
		QSqlDatabase::database().rollback();
		throw e;
	}

	int visit_id = query.value(0).toInt();
	sql = "insert into reservation (visit_id,start_date,end_date,note) values (:visit_id,:start_date,:end_date,:note) returning reserv_id";
	query.prepare(sql);
	query.bindValue(":visit_id",visit_id);
	query.bindValue(":start_date",*startDate);
	query.bindValue(":end_date",*endDate);
	query.bindValue(":note",note);
	if(!query.exec())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not insert into reservations. " + query.lastError().text());
		QSqlDatabase::database().rollback();
		throw e;
	}

	if(!query.next())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not query the reserv_id " + query.lastError().text());
		QSqlDatabase::database().rollback();
		throw e;
	}

	int reserv_id = query.value(0).toInt();

	Visit* visit = new Visit(visit_id,file_nr,*startDate,false,false);
	Schedule* s = new Schedule(reserv_id,startDate,endDate,visit,note);
	try{
		sm->addSchedule(s);
	}
	catch(Exception e)
	{
		QSqlDatabase::database().rollback();
		throw e;
	}
	QSqlDatabase::database().commit();
}

void korlapAdmin_model::deleteSchedules(QList<Schedule*> list)
{
	db->transaction();
	QSqlQuery query;
	QString sql = "DELETE FROM RESERVATION WHERE";
	this->qb.initialize(sql,"reserv_id");
	Schedule* s;
	QueryBuilder qb2;
	qb2.initialize("DELETE FROM VISIT WHERE","VISIT_ID");
	foreach(s,list)
	{
		if(s)
		{
			qb.append(QString::number(s->getId()));
			if(!s->getVisit()->isEdited())
			{
				qb2.append(QString::number(s->getVisit()->getVisitId()));
			}
		}
	}

	if(!qb.count())
		return;
	query.prepare(qb.query());
	if(!query.exec())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not delete reservation. " + query.lastError().text());
		db->rollback();
		throw e;
	}

	if(qb2.count())
	{
		query.prepare(qb2.query());
		if(!query.exec())
		{
			CouldNotExecQueryException e;
			e.setMessage("Could not delete visit. " + query.lastError().text());
			db->rollback();
			throw e;
		}
	}

	foreach(s,list)
	{
		Visit* v = s->getVisit();
		sm->deleteSchedule(s);
		if(v && !v->isEdited())
		{
			vm->deleteVisit(v);
		}	
	}

	db->commit();
}

void korlapAdmin_model::deleteSchedule(Schedule* s)
{
	db->transaction();
	QSqlQuery query;
	QString sql = "DELETE FROM RESERVATION WHERE RESERV_ID= :reserv_id";
	query.prepare(sql);
	query.bindValue(":reserv_id",s->getId());
	if(!query.exec())
	{
		CouldNotExecQueryException e;
		e.setMessage("Could not delete reservation. " + query.lastError().text());
		db->rollback();
		throw e;
	}

	if(!s->getVisit()->isEdited())
	{
		sql = "DELETE FROM VISIT WHERE VISIT_ID = :visit_id";
		query.prepare(sql);
		query.bindValue(":visit_id",s->getVisit()->getVisitId());
		if(!query.exec())
		{
			CouldNotExecQueryException e;
			e.setMessage("Could not delete visit. " + query.lastError().text());
			db->rollback();
			throw e;
		}
	}
	sm->deleteSchedule(s);
	db->commit();
}

void korlapAdmin_model::updateSchedule(Schedule* s, const QString& note)
{
	sm->updateSchedule(s,note);
}

Schedule* korlapAdmin_model::getSchedule(QDateTime *time)
{
	Schedule* s = sm->getScheduleByTime(time->toTime_t());//getScheduleMapForThisWeek()->value(*it);// sm->getSchedules()->value(time);
	if(s == NULL)
		return s;
	if(*time >= *s->getStartDate() && *time <= *s->getEndDate())
		return s;
	else return NULL;
}
const Filter* korlapAdmin_model::getDefaultFilter()
{
	return this->fm->getDefaultFilter();
}

QList<int> korlapAdmin_model::addPatient(unsigned int fileno, const QString& name, const QString& phone,QDate date)
{
	Patient* p = new Patient(fileno,name,phone,date);
	return pm->addPatient(p);
}

PatientManager* korlapAdmin_model::getPatientManager()
{
	return this->pm;
}

FilterManager* korlapAdmin_model::getFilterManager()
{
	return this->fm;
}
